#ifndef SC_THREAD_H
#define SC_THREAD_H

/* 多线程相关的功能 */

#include "common.h"

#include <stdatomic.h>
#include <stdbool.h>

#include "tick.h"

/*
Forward declarations-前向声明
前向声明是利用了编译器的特性，编译器在编译的过程中只需要知道各个元素的名称和相应的大小就可以
也就是此处的定义并没有引用 SDL2库中的 SDL_thread.h，这个 SDL_thread.h 文件在本项目 thread.c 中才有引用
typedef  oldName  newName;
*/
typedef struct SDL_Thread SDL_Thread;
typedef struct SDL_mutex SDL_mutex;
typedef struct SDL_cond SDL_cond;

/* 定义函数指针类型 sc_thread_fn */
typedef int sc_thread_fn(void *);

// 自定义线程ID-无符号整形
typedef unsigned sc_thread_id;

// 自定义原子线程ID-原子无符号整形
typedef atomic_uint sc_atomic_thread_id;

/* 自定义的 SDL_Thread线程结构的包装结构 */
typedef struct sc_thread {
    SDL_Thread *thread;
} sc_thread;

// 自定义线程优先级
enum sc_thread_priority {
    SC_THREAD_PRIORITY_LOW,
    SC_THREAD_PRIORITY_NORMAL,
    SC_THREAD_PRIORITY_HIGH,
    // 实时的
    SC_THREAD_PRIORITY_TIME_CRITICAL,
};

/* 自定义互斥锁,对SDL_mutex进行简单包装 */
typedef struct sc_mutex {
    // 用于保证在任意时刻只有一个线程可以进入临界区执行代码，其他线程会被阻塞直到锁被释放。
    SDL_mutex *mutex;
    /* #define NDEBUG 来禁用assert调用 */
#ifndef NDEBUG
    // 存储加锁的线程ID,调试时没有这个变量。解锁时会判断是不是同一个线程的操作
    sc_atomic_thread_id locker;
#endif
} sc_mutex;

/* 自定义信号量，对SDL_cond简单包装 */
typedef struct sc_cond {
    SDL_cond *cond;
} sc_cond;

// 主线程ID，main线程的ID
extern sc_thread_id SC_MAIN_THREAD_ID;

/*
创建一个后台线程并启动，对 SDL_CreateThread 的包装
@param thread 创建成功的线程信息将回写到这个参数中去
@param fn 后台线程要执行的方法，函数指针
@param name 线程的名称，最长15个字符，不可为NULL
@param userdata 是要传给 fn方法 的参数
@return 成功返回true,失败返回false
*/
bool
sc_thread_create(sc_thread *thread, sc_thread_fn fn, const char *name,
                 void *userdata);

/*
等待一个线程的完成,对SDL_WaitThread的简单封装
只有一个线程可以在另一个线程上调用SDL_WaitThread()。线程指针由这个函释放，之后无效
@param thread 等待的线程id
@param status 回写线程的退出状态，传入NULL忽略线程返回的状态
*/
void
sc_thread_join(sc_thread *thread, int *status);

/// @brief 设置当前线程的优先级
/// @param  自定义线程优先级
/// @return 设置成功返回true,否则返回false
bool
sc_thread_set_priority(enum sc_thread_priority priority);

/*
初始化一个自定义互斥锁，初始化完成时是解锁状态
@param mutex 自定义互斥锁
@return 成功返回true，失败返回false
*/
bool
sc_mutex_init(sc_mutex *mutex);

// 销毁一个互斥锁,回收资源
// @param mutex 自定义互斥锁
void
sc_mutex_destroy(sc_mutex *mutex);

/*
加锁
@param mutex
*/
void
sc_mutex_lock(sc_mutex *mutex);

/*
解锁
@param mutex
*/
void
sc_mutex_unlock(sc_mutex *mutex);

/*
 * 返回当前线程id，注意与getpid()返回的ID是不同的
 * @return 成功返回 0,失败返回 非零值
 */
sc_thread_id
sc_thread_get_id(void);

#ifndef NDEBUG
/*
判断加锁与解锁的线程是否一致
@param mutex
@return 线程一致返回true，否则返回false
*/
bool
sc_mutex_held(struct sc_mutex *mutex);
# define sc_mutex_assert(mutex) assert(sc_mutex_held(mutex))
#else
// 空实现
# define sc_mutex_assert(mutex)
#endif

/*
初始化一个条件信号量
@param cond
@return
*/
bool
sc_cond_init(sc_cond *cond);

/*
销毁并清理条件信号量，SDL_DestroyCond的简单封装
@param cond
*/
void
sc_cond_destroy(sc_cond *cond);

// SDL_CondWait的简单封装
// @param cond
// @param mutex
void
sc_cond_wait(sc_cond *cond, sc_mutex *mutex);

// @param cond
// @param mutex
// @param deadline 以开机后的毫秒数时间戳为准
// return true on signaled, false on timeout
bool
sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, sc_tick deadline);

// 唤醒正在等待条件变量发生改变的线程。SDL_CondSignal的简单封装
// @param cond
void
sc_cond_signal(sc_cond *cond);

// SDL_CondBroadcast的简单封装
// @param cond
void
sc_cond_broadcast(sc_cond *cond);

#endif
